<template>
  <div
    class="threeDialog"
    v-loading="loading"
    element-loading-background="rgba(0, 0, 0, 1)">
    <el-icon class="gb" @click="index_gb()" color="white" size="20">
      <CloseBold />
    </el-icon>
    <div class="three-class" id="threeShow" v-if="!isError"></div>
    <div class="error-class" v-if="isError">
      <img :src="Faile" />
    </div>
  </div>
</template>
<script setup>
import { onMounted, inject, ref } from 'vue'
import * as THREE from 'three'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import Faile from '../../assets/images/faile.png'
const props = defineProps({
  url1: {
    type: String,
    default: () => {
      return ''
    }
  },
  title: {
    type: String,
    default: () => {
      return ''
    }
  }
})

//父组件方法
const index_gb = inject('index_gb')

const loading = ref(false)
const isError = ref(false)

onMounted(() => {
  loading.value = true
  //监听键盘事件
  window.addEventListener('keydown', (e) => {
    // esc按键
    if (e.keyCode === 27) {
      // 关闭弹窗
      index_gb()
    }
  })
  if (props.url1) {
    init()
  } else {
    isError.value = true
  }
})
let model, scene, camera

const init = () => {
  let box = document.getElementById('threeShow')

  //创建场景
  scene = new THREE.Scene()

  //背景颜色
  // scene.background = new THREE.Color("#fff");
  // const texture = new THREE.TextureLoader().load(View);
  // scene.background = texture;

  //创建相机
  camera = new THREE.PerspectiveCamera(
    15,
    window.innerWidth / window.innerHeight,
    1,
    1000
  )
  //相机位置
  camera.position.set(0, 0, 50)
  //将相机加入到场景中
  scene.add(camera)

  // 增加坐标系红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
  // 显示三维坐标系，辅助找旋转角度
  // const axes = new THREE.AxesHelper(20);
  // 添加坐标系到场景中
  // scene.add(axes);

  //创建渲染器
  let render = new THREE.WebGLRenderer({
    antialias: true, //设置抗锯齿，防失真
    alpha: true //完全透明,没有背景色
  })
  //设置渲染器大小，一般就是容器高度和宽度
  render.setSize(window.innerWidth, window.innerHeight)
  //如果设置开启，允许在场景中使用阴影贴图。默认是 false。
  render.shadowMap.enabled = true
  //执行渲染操作
  render.render(scene, camera)
  //将渲染器添加到容器中 box-容器
  box.append(render.domElement)

  //添加轨道控制器
  const controls = new OrbitControls(camera, render.domElement)
  // 控制旋转的角度
  controls.minPolarAngle = Math.PI / 150 //绕Y轴旋转的最小角度
  // controls.maxPolarAngle = 3 * Math.PI / 7;  //绕Y轴旋转的最大角度
  // controls.enableRotate = false; // 禁用旋转

  //模型加载器
  const loader = new GLTFLoader()
  const dracoLoader = new DRACOLoader()
  dracoLoader.setDecoderPath('/draco/')
  dracoLoader.setDecoderConfig({ type: 'js' }) //使用js方式解压
  dracoLoader.preload() //初始化_initDecoder 解码器
  loader.setDRACOLoader(dracoLoader) //gltfloader使用dracoLoader
  //加载模型
  loader.load(
    props.url1,
    (gltf) => {
      model = gltf.scene
      //设置模型初始比例
      // model.scale.set(1, 1, 1);
      model.position.set(0, 0, 0)
      model.rotation.y = Math.PI
      model.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
          child.castShadow = true //阴影
          child.receiveShadow = true //接受别人投的阴影
          child.castShadow = true //灯光开启投射阴影
        }
      })
      //把模型加入到场景
      scene.add(model)
      getModelMeaterialList()
      createLight()
      loading.value = false
    },
    // 模型加载中
    function (xhr) {
      let num = ((xhr.loaded / xhr.total) * 100).toFixed(2) + '%'
    },
    // 模型加载错误时
    function (error) {
      console.error(error, 'error')
      loading.value = false
      isError.value = true
    }
  )

  function renderFn() {
    //浏览器自带的函数 每秒60帧更新
    requestAnimationFrame(renderFn)
    //开始渲染模型和dom元素
    render.render(scene, camera)
    //更新轨道控制器
    controls.update()
  }
  renderFn()

  // 添加窗口变化监听器-canvas画布自适应变化
  addEventListener('resize', () => {
    // 更新修改相机比例
    camera.aspect = window.innerWidth / window.innerHeight
    // 更新摄像机的投影矩阵
    camera.updateProjectionMatrix()
    // 更新画布大小
    render.setSize(
      window.innerWidth, // 宽度
      window.innerHeight // 高度
    )
    // 更新画布像素比
    render.setPixelRatio(window.devicePixelRatio)
  })
}
// 获取当前模型材质-转换材质有效提高性能
function getModelMeaterialList() {
  let i = 0
  model.traverse((v) => {
    if (v) {
      v.castShadow = true
      v.frustumCulled = false
      i++
      if (v.material) {
        const { name, color, map } = v.material
        // 统一将模型材质 设置为 MeshLambertMaterial 类型
        if (v) {
          //最不好的办法就是修改这里的 要求option透明度 v.material.opacity == 1 等于1才变换材质但是很卡
          v.material = new THREE.MeshPhysicalMaterial({
            map,
            transparent: true,
            color,
            name,
            opacity: v.material.opacity,
            emissiveIntensity: v.material.emissiveIntensity,
            emissive: v.material.emissive,
            // 控制玻璃材质两个的属性
            //alphaTest: 0.6,
            depthWrite: true
          })
          if(v.material.opacity != 1){
              //把具有透明材质的物体优先渲染 
              v.renderOrder = 1
            }
        }
        //开启双面渲染
        v.material.side = THREE.DoubleSide
      }
    }
  })
}
// 创建光源
function createLight() {
  const light_intensity = 0.2
  // 直线光
  let light1 = new THREE.DirectionalLight(0xffffff, 0.1)
  light1.position.set(-2.355, 2.977, 0.15)
  //光线辅助器
  let light2 = new THREE.DirectionalLight(0xffffff, light_intensity)
  light2.position.set(2.951, 2.0, 1)

  //平行光
  let light3 = new THREE.AmbientLight(0xffffff, light_intensity)
  light3.position.set(1.437, 2.863, -7.656)

  // 半球光
  let light4 = new THREE.HemisphereLight(0xffffff, 0xffffff, light_intensity)
  light4.position.set(0, 2, 0)
  //光线辅助器
  let light5 = new THREE.DirectionalLight(0xffffff, light_intensity)
  light5.position.set(1.951, 2.0, -6)

  scene.add(light1)
  scene.add(light2)
  scene.add(light3)
  scene.add(light4)
  scene.add(light5)
}
</script>
<style scoped lang="scss">
.threeDialog {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgb(0, 0, 0, 0.8);
  z-index: 9999;

  .gb {
    position: absolute;
    top: 30px;
    right: 30px;
    color: white;
    font-size: 20px;
    z-index: 2001;
  }

  .three-class {
    width: 100%;
    height: 100%;
    background: url(../../assets/images/view.png) no-repeat;
    background: rgba(0, 0, 0, 0.3);
    background-size: 100% 100%;
  }

  :deep(.el-dialog.is-fullscreen) {
    background: transparent;
  }

  .error-class {
    position: absolute;
    top: 40%;
    left: 43%;
    transform: rotate(-50%, -50%);
    color: #e6a23c;
    font-size: 12px;
    text-align: center;
  }
}
</style>
